﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.IO;
using System.Reflection;
using Abp.Collections.Extensions;
using Abp.Localization;
using Magicodes.ExcelImporter.Attributes;
using Magicodes.ExcelImporter.Common;
using Magicodes.ExcelImporter.Models;
using Magicodes.Plus.Application.Dto;
using Magicodes.Plus.Core;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace Magicodes.ExcelImporter
{
    public class ExcelImporter : IExcelImporter
    {
        private readonly ILocalizationManager _localizationManager;

        public ExcelImporter(ILocalizationManager localizationManager)
        {
            _localizationManager = localizationManager;
        }

        public IAppFolders AppFolders { get; set; }

        /// <summary>
        ///     下载模板
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns>文件</returns>
        public FileDto GetGenerateTemplate<T>(string fileName)
        {
            var excelFileName = fileName + ".xlsx";
            return CreateExcelPackage(excelFileName, excelPackage =>
            {
                var worksheet = excelPackage.Workbook.Worksheets.Add(fileName);
                worksheet.OutLineApplyStyle = true;
                var importerTableDto = GetImporterTableDto<T>();
                List<ImporterHeaderDto> importerHeaderDtos;
                if (GetImporterHeaderDtoList<T>(out importerHeaderDtos)) return;
                AddHeader(importerHeaderDtos, worksheet, importerTableDto);
                AddStyle(importerHeaderDtos, worksheet);
                worksheet.View.FreezePanes(2, 1);
            });
        }

        /// <summary>
        ///     创建Excel页
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="creator"></param>
        /// <returns></returns>
        protected FileDto CreateExcelPackage(string fileName, Action<ExcelPackage> creator)
        {
            var file = new FileDto(fileName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

            using (var excelPackage = new ExcelPackage())
            {
                creator(excelPackage);
                Save(excelPackage, file);
            }
            return file;
        }

        /// <summary>
        ///     创建表头
        /// </summary>
        /// <param name="importerHeaderDtos"></param>
        /// <param name="worksheet"></param>
        /// <param name="importerTableDto"></param>
        protected void AddHeader(List<ImporterHeaderDto> importerHeaderDtos, ExcelWorksheet worksheet,
            ImporterTableDto importerTableDto)
        {
            if (importerTableDto.LocalizationSource != null)
            {
                foreach (var headerDto in importerHeaderDtos)
                    if (headerDto != null && headerDto.Attribute.Length > 0)
                    {
                        var importerHeaderAttribute = headerDto.Attribute[0];
                        if (importerHeaderAttribute != null)
                        {
                            worksheet.Cells[1, headerDto.Index].Value =
                                _localizationManager.GetString(importerTableDto.LocalizationSource,
                                    headerDto.PropertyName);
                            worksheet.Cells[1, headerDto.Index].Style.Font.Bold = importerHeaderAttribute.Bold;
                            worksheet.Cells[1, headerDto.Index].Style.Font.Size =
                                importerHeaderAttribute.FontSize;
                            worksheet.Cells[1, headerDto.Index].Style.Fill.PatternType = ExcelFillStyle.Solid;
                            worksheet.Cells[1, headerDto.Index].Style.Fill.BackgroundColor.SetColor(
                                ColorTranslator.FromHtml("#00FF00"));
                        }
                    }
            }
            else
            {
                foreach (var headerDto in importerHeaderDtos)
                    if (headerDto != null && headerDto.Attribute.Length > 0)
                    {
                        var importerHeaderAttribute = headerDto.Attribute[0];
                        if (headerDto.Attribute[0] != null)
                        {
                            worksheet.Cells[1, headerDto.Index].Value = headerDto.PropertyName;
                            worksheet.Cells[1, headerDto.Index].Style.Font.Bold = importerHeaderAttribute.Bold;
                            worksheet.Cells[1, headerDto.Index].Style.Font.Size =
                                importerHeaderAttribute.FontSize;
                            worksheet.Cells[1, headerDto.Index].Style.Fill.PatternType = ExcelFillStyle.Solid;
                            worksheet.Cells[1, headerDto.Index].Style.Fill.BackgroundColor.SetColor(
                                ColorTranslator.FromHtml("#00FF00"));
                        }
                    }
            }
        }

        /// <summary>
        ///     添加样式
        /// </summary>
        /// <param name="importerHeaderDtos"></param>
        /// <param name="worksheet"></param>
        protected void AddStyle(List<ImporterHeaderDto> importerHeaderDtos, ExcelWorksheet worksheet)
        {
            foreach (var importerHeaderDto in importerHeaderDtos)
            {
                if (importerHeaderDto.PropertyType != null)
                {
                    if (importerHeaderDto.PropertyType.Name.ToLower() == "boolean")
                    {
                        var boolDataValidations =
                            worksheet.DataValidations.AddListValidation(
                                worksheet.Cells[2, importerHeaderDto.Index].Address + ":" +
                                worksheet.Cells[5000, importerHeaderDto.Index].Address);
                        boolDataValidations.Formula.Values.Add("是");
                        boolDataValidations.Formula.Values.Add("否");
                    }
                    if (importerHeaderDto.PropertyType.BaseType?.Name.ToLower() == "enum")
                    {
                        var enumDataValidations =
                            worksheet.DataValidations.AddListValidation(
                                worksheet.Cells[2, importerHeaderDto.Index].Address + ":" +
                                worksheet.Cells[5000, importerHeaderDto.Index].Address);
                        var enumDisplayNames = EnumHelper.GetEnumDescs(importerHeaderDto.PropertyType);
                        foreach (var enumDisplayName in enumDisplayNames.Keys)
                            enumDataValidations.Formula.Values.Add(enumDisplayName);
                    }
                }
                if (importerHeaderDto.Attribute.Length > 0)
                {
                    var importerHeaderAttribute = importerHeaderDto.Attribute[0];
                    if (importerHeaderAttribute != null)
                    {
                        worksheet.Column(importerHeaderDto.Index).Style.Numberformat.Format =
                            importerHeaderAttribute.Format;
                        if (importerHeaderAttribute.AutoFit)
                            worksheet.Column(importerHeaderDto.Index).AutoFit();
                        if (importerHeaderAttribute.Required)
                            worksheet.Column(importerHeaderDto.Index).Style.Font.Color.SetColor(ColorTranslator.FromHtml("#FF0000"));
                    }
                }
            }
        }

        /// <summary>
        ///     保存
        /// </summary>
        /// <param name="excelPackage"></param>
        /// <param name="file"></param>
        protected void Save(ExcelPackage excelPackage, FileDto file)
        {
            var filePath = Path.Combine(AppFolders.TempFileDownloadFolder, file.FileToken);
            excelPackage.SaveAs(new FileInfo(filePath));
        }

        private static bool GetImporterHeaderDtoList<T>(out List<ImporterHeaderDto> importerHeaderDtos)
        {
            importerHeaderDtos = new List<ImporterHeaderDto>();
            var objProperties = typeof(T).GetProperties();
            if (objProperties.IsNullOrEmpty())
                return true;
            for (var i = 0; i < objProperties.Length; i++)
                importerHeaderDtos.Add(new ImporterHeaderDto
                {
                    Index = i + 1,
                    PropertyName = objProperties[i].Name,
                    PropertyType = objProperties[i].PropertyType,
                    Attribute =
                        objProperties[i].GetCustomAttributes(typeof(ImporterHeaderAttribute), true) as
                            ImporterHeaderAttribute[]
                });
            return false;
        }

        private static ImporterTableDto GetImporterTableDto<T>()
        {
            var importerTableDto = new ImporterTableDto();
            var importerTableAttributes = typeof(T).GetCustomAttributes(typeof(ImporterTableAttribute), true);
            foreach (ImporterTableAttribute importerTableAttribute in importerTableAttributes)
                if (importerTableAttribute != null)
                {
                    importerTableDto.LocalizationSource = importerTableAttribute.LocalizationSource;
                    break;
                }
            return importerTableDto;
        }

        #region 从Excel中加载数据（泛型）+IEnumerable<T> LoadFromExcel<T>(string FileName) where T : new()

        /// <summary>
        ///     从Excel中加载数据（泛型）
        /// </summary>
        /// <typeparam name="T">每行数据的类型</typeparam>
        /// <param name="stream">Excel文件流</param>
        /// <returns>泛型列表</returns>
        public ImportResultDto<T> LoadFromExcel<T>(Stream stream) where T : new()
        {
            var isValid = true;
            var resultList = new List<T>();
            var validationErrorResultDtos = new List<ValidationErrorResultDto>();
            var dictHeader = new Dictionary<string, int>();

            using (var package = new ExcelPackage(stream))
            {
                var worksheet = package.Workbook.Worksheets[1];

                var colStart = worksheet.Dimension.Start.Column;
                var colEnd = worksheet.Dimension.End.Column;
                var rowStart = worksheet.Dimension.Start.Row;
                var rowEnd = worksheet.Dimension.End.Row;

                for (var i = colStart; i <= colEnd; i++)
                    dictHeader[worksheet.Cells[rowStart, i].Value.ToString()] = i;
                var propertyInfoList = new List<PropertyInfo>(typeof(T).GetProperties());
                var importerTableDto = GetImporterTableDto<T>();
                for (var row = rowStart + 1; row <= rowEnd; row++)
                {
                    var result = new T();
                    foreach (var p in propertyInfoList)
                        try
                        {
                            var cell = worksheet.Cells[row,dictHeader[_localizationManager.GetString(importerTableDto.LocalizationSource, p.Name)]];
                            if (cell.Value == null)
                            {
                                if (p.GetCustomAttribute<RequiredAttribute>() != null)
                                {
                                    isValid = false;
                                    var errorString = _localizationManager.GetString(
                                        importerTableDto.LocalizationSource, "ImportDataValidationError");
                                    validationErrorResultDtos.Add(new ValidationErrorResultDto
                                    {
                                        Errors =
                                            string.Format(errorString, cell.Start.Row,
                                                _localizationManager.GetString(importerTableDto.LocalizationSource,
                                                    p.Name))
                                    });
                                }
                                continue;
                            }
                            switch (p.PropertyType.Name.ToLower())
                            {
                                case "string":
                                    p.SetValue(result, cell.GetValue<string>());
                                    break;
                                case "int16":
                                    p.SetValue(result, cell.GetValue<short>());
                                    break;
                                case "int32":
                                    p.SetValue(result, cell.GetValue<int>());
                                    break;
                                case "int64":
                                    p.SetValue(result, cell.GetValue<long>());
                                    break;
                                case "decimal":
                                    p.SetValue(result, cell.GetValue<decimal>());
                                    break;
                                case "double":
                                    p.SetValue(result, cell.GetValue<double>());
                                    break;
                                case "datetime":
                                    p.SetValue(result, cell.GetValue<DateTime>());
                                    break;
                                case "boolean":
                                    cell.Value = cell.Value.ToString() == "是" ? 1 : 0;
                                    p.SetValue(result, cell.GetValue<bool>());
                                    break;
                                case "byte":
                                    p.SetValue(result, cell.GetValue<byte>());
                                    break;
                                case "char":
                                    p.SetValue(result, cell.GetValue<char>());
                                    break;
                                case "single":
                                    p.SetValue(result, cell.GetValue<float>());
                                    break;
                                default:
                                    break;
                            }
                            switch (p.PropertyType.BaseType?.Name.ToLower())
                            {
                                case "enum":
                                    var enumDisplayNames = EnumHelper.GetEnumDescs(p.PropertyType);
                                    cell.Value = enumDisplayNames[cell.Value.ToString()];
                                    p.SetValue(result, cell.GetValue<int>());
                                    break;
                            }
                        }
                        catch (KeyNotFoundException ex)
                        {
                            return new ImportResultDto<T>
                            {
                                IsValid = false,
                                Data = resultList,
                                ValidationErrorResults = new List<ValidationErrorResultDto>() { new ValidationErrorResultDto(){Errors = ex.Message}}
                            };
                        }
                    resultList.Add(result);
                }
            }
            return new ImportResultDto<T>
            {
                IsValid = isValid,
                Data = resultList,
                ValidationErrorResults = validationErrorResultDtos
            };
        }

        #endregion
    }
}